package com.zzyl.nursing.service.impl;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.nursing.websocket.WebSocketServer;
import com.zzyl.system.mapper.SysUserMapper;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.bcel.classfile.Constant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 报警规则表Service业务层处理
 *
 * @author ruoyi
 * @date 2025-09-22
 */
@Slf4j
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private IAlertDataService alertDataService;

    @Autowired
    private WebSocketServer webSocketServer;

    @Value("${alert.deviceMaintainerRole}")
    private String deviceMaintainerRoleName;
    @Value("${alert.managerRole}")
    private String managerRoleName;

    /**
     * 查询报警规则表
     *
     * @param id 报警规则表主键
     * @return 报警规则表
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return getById(id);
    }

    /**
     * 查询报警规则表列表
     *
     * @param alertRule 报警规则表
     * @return 报警规则表
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则表
     *
     * @param alertRule 报警规则表
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则表
     *
     * @param alertRule 报警规则表
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则表
     *
     * @param ids 需要删除的报警规则表主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则表信息
     *
     * @param id 报警规则表主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 1.报警数据过滤
     */
    @Override
    public void deviceDataAlertFilter() {
        //1.查询所有启动的报警规则
        Long count = this.lambdaQuery().eq(AlertRule::getStatus, 1).count();
        //2.如果为空，则结束定时任务
        if (count == 0) {
            return;
        }
        //3.如果不为空从从Redis数据库获取最新上报的数据集合(deviceData)
        List<Object> deviceDataList = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        //4.如果为空，则结束定时任务
        if (ObjectUtil.isEmpty(deviceDataList)) {
            return;
        }
        //5.如果不为空，则将最新上报的数据集合放到一个待处理的集合中
        List<DeviceData> waitCheckList = new ArrayList<>();
        deviceDataList.forEach(item -> {
            waitCheckList.addAll((List<DeviceData>) item);
        });
        if (ObjectUtil.isEmpty(waitCheckList)) {
            return;
        }
        //6.遍历待处理的集合,查看每条数据是否满足报警规则
        waitCheckList.forEach(deviceData -> alertCheck(deviceData));

    }

    /**
     * 2.报警检测
     *
     * @param deviceData
     */
    private void alertCheck(DeviceData deviceData) {
        //1.如果该上报属性数据超过一分钟了，则结束
        LocalDateTime alarmTime = deviceData.getAlarmTime();
        long betweenSenconds = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        if (betweenSenconds > 60) {
            return;
        }
        //2.查询该设备的通用报警规则
        List<AlertRule> alertRuleList = this.lambdaQuery().eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getIotId, "-1")
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .eq(AlertRule::getStatus, 1)
                .list();
        //3.查询该设备的特定报警规则
        List<AlertRule> alertRuleList2 = this.lambdaQuery().eq(AlertRule::getProductKey, deviceData.getProductKey())
                .eq(AlertRule::getIotId, deviceData.getIotId())
                .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                .eq(AlertRule::getStatus, 1)
                .list();
        //4.合并通用报警规则和特定报警规则
        Collection<AlertRule> alertRules = CollUtil.addAll(alertRuleList, alertRuleList2);

        //5.判断规则集合是否为空，如果为空，则结束
        if (ObjectUtil.isEmpty(alertRules)) {
            return;
        }
        //6.不为空，遍历每一条规则，对该上报数据进行检测
        alertRules.forEach(alertRule -> deviceDataRultCheck(alertRule, deviceData)
        );
    }

    private void deviceDataRultCheck(AlertRule alertRule, DeviceData deviceData) {
        //判断上报数据时间是否在报警规则的生效时段内(00:00:00~23:59:59)
        String[] split = alertRule.getAlertEffectivePeriod().split("~");
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);
        LocalTime localTime = deviceData.getAlarmTime().toLocalTime();
        if (localTime.isBefore(startTime) || localTime.isAfter(endTime)) {
            return;
        }
        //判断上报数据值是否满足报警规则阈值
        String alertCountKey = CacheConstants.IOT_COUNT_ALERT + deviceData.getIotId() + ":" + deviceData.getFunctionId() + ":" + alertRule.getId();
//        int compare1 = Double.valueOf(deviceData.getDataValue()).compareTo(alertRule.getValue());
        int compare = NumberUtil.compare((Double.valueOf(deviceData.getDataValue())), alertRule.getValue());
        if (">=".equals(alertRule.getOperator()) && compare >= 0 || "<".equals(alertRule.getOperator()) && compare < 0) {
            log.warn("报警规则： {}" + alertRule.getAlertRuleName() + "满足条件");
        } else {
            redisTemplate.delete(alertCountKey);
            return;
        }
        //判断是否在沉默周期内，在沉默周期内则结束
        //组织沉默周期key
        String alertSilentKey = CacheConstants.IOT_SILENT_ALERT + deviceData.getIotId() + ":" + deviceData.getFunctionId() + ":" + alertRule.getId();
        if (redisTemplate.hasKey(alertSilentKey)) {
            return;
        }
        //如果不在沉默周期内则报警次数加1，判断是否满足持续周期，满足则报警
        Long increment = redisTemplate.opsForValue().increment(alertCountKey);
        if (increment < alertRule.getDuration()) {
            return;
        }

        //满足持续周期，删除报警次数
        redisTemplate.delete(alertCountKey);
        //设置沉默周期
        redisTemplate.opsForValue().set(alertSilentKey, "1", alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);
//        redisTemplate.opsForValue().set(alertSilentKey, "1", Duration.ofMinutes(alertRule.getAlertSilentPeriod()));

        //通知报警人员
        List<Long> userIds = new ArrayList<>();
        if (alertRule.getAlertDataType() == 0) {
            if (deviceData.getLocationType() == 0) {
                userIds = deviceMapper.selectNursingIdByIotIdWithElder(deviceData.getIotId());
            } else if (deviceData.getLocationType() == 1 && deviceData.getPhysicalLocationType() == 2) {
                userIds = deviceMapper.selectNursingIdByIotIdWithBed(deviceData.getIotId());
            }
        }else {
            userIds = sysUserRoleMapper.selectByRoleName(deviceMaintainerRoleName);
        }
        List<Long> adminUserIds = sysUserRoleMapper.selectByRoleName(managerRoleName);

        List<Long> needNotifyUserIds = CollUtil.distinct(CollUtil.addAll(userIds, adminUserIds));

        //保存报警数据到AlertData表
        AlertData alertData = batchSaveAlertData(deviceData, alertRule, needNotifyUserIds);

        //报警通知
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertData, AlertNotifyVo.class);
        alertNotifyVo.setFunctionName(alertRule.getFunctionName());
        alertNotifyVo.setNotifyType(alertRule.getAlertDataType());
        alertNotifyVo.setNotifyType(1);

        //发送报警通知
        webSocketServer.sendMessageToConsumer(alertNotifyVo,needNotifyUserIds );
    }

    /**
     * 批量保存报警数据
     *
     * @param deviceData
     * @param alertRule
     * @param needNotifyUserIds
     */
    private AlertData batchSaveAlertData(DeviceData deviceData, AlertRule alertRule, List<Long> needNotifyUserIds) {
        log.error("通知养老院的工作人员:{}", needNotifyUserIds);
        //属性拷贝
        AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
        alertData.setId(null);
        //属性填充
        alertData.setAlertRuleId(alertRule.getId());
        //alertData.setAlertReason(alertRule.getFunctionName() + alertRule.getOperator() + alertRule.getValue() + "个周期,持续" + alertRule.getDuration() + "分钟");
        StringBuilder sb = new StringBuilder();
        sb.append(alertRule.getFunctionName())
                        .append(alertRule.getOperator())
                        .append(alertRule.getValue()).append("持续")
                        .append(alertRule.getDuration())
                                .append("个周期,沉默")
                .append(alertRule.getAlertSilentPeriod()).append("分钟");
        alertData.setAlertReason(sb.toString());

        alertData.setType(alertRule.getAlertDataType());
        alertData.setStatus(0);
        //循环待通知人Id组装集合
        List<AlertData> collect = needNotifyUserIds.stream()
                .map(userId ->
                        BeanUtil.toBean(alertData, AlertData.class).setUserId(userId))
                .collect(Collectors.toList());
        //批量保存
        this.alertDataService.saveBatch(collect);
        return alertData;
    }
}
